package com.javaweb.web.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.javaweb.base.BaseDao;
import com.javaweb.base.po.Module;
import com.javaweb.base.po.Role;
import com.javaweb.base.po.RoleModule;
import com.javaweb.base.po.RoleRestrict;
import com.javaweb.base.po.User;
import com.javaweb.base.po.UserRole;
import com.javaweb.constant.CommonConstant;
import com.javaweb.constant.SystemConstant.AdminConstant;
import com.javaweb.db.query.QueryWapper;
import com.javaweb.util.core.ObjectOperateUtil;
import com.javaweb.util.core.StringUtil;
import com.javaweb.util.entity.Page;
import com.javaweb.web.eo.roleRestrict.ProvinceCityDistrictAllResponse;
import com.javaweb.web.eo.roleRestrict.RoleRestrictAddRequest;
import com.javaweb.web.eo.roleRestrict.RoleRestrictListRequest;
import com.javaweb.web.eo.roleRestrict.RoleRestrictListResponse;
import com.javaweb.web.eo.roleRestrict.RoleRestrictModifyRequest;
import com.javaweb.web.eo.roleRestrict.RoleRestrictResponse;
import com.javaweb.web.service.RoleRestrictService;

/**
 * 角色限定模块业务层接口实现
 * 
 * @author tank2140896
 */
@Service("roleRestrictServiceImpl")
public class RoleRestrictServiceImpl extends BaseDao implements RoleRestrictService {

	/**
	 * 判断角色限定是否存在交集业务层接口实现
	 * 
	 * @param province 省
	 * @param city 市
	 * @param district 区
	 * @param roleId 角色ID
	 * @param restrictWay 角色限定方式（参见RoleRestrict#restrictWay）
	 * @return boolean true：存在交集；false：不存在交集
	 */
	@Override
	public boolean roleRestrictIntersect(String province,String city,String district,String roleId,Integer restrictWay) {
		if(restrictWay==1){//限定本人（如果已经限定了本人，就不能再去限定本人（同一个角色只能限定一次本人）或非本人（限定本人和限定非本人为互斥））
			QueryWapper<RoleRestrict> queryWapper = new QueryWapper<>();
			queryWapper.eq(RoleRestrict::getRoleId,roleId);
            Long count = roleRestrictDao.selectListCount(queryWapper);
            if(count!=null&&count.longValue()>0){
                return true;
            }
            return false;
		}else{//限定非本人
			QueryWapper<RoleRestrict> queryWapper = new QueryWapper<>();
			queryWapper.eq(RoleRestrict::getRoleId,roleId);
			queryWapper.eq(RoleRestrict::getRestrictWay,2);
            List<RoleRestrict> roleRestrictList = roleRestrictDao.selectList(queryWapper);
            if(!CollectionUtils.isEmpty(roleRestrictList)){
            	for(RoleRestrict each:roleRestrictList){
                    //三者都相同，肯定是交集
                    if((each.getProvince().equals(province))&&(each.getCity().equals(city))&&(each.getDistrict().equals(district))){
                    	return true;
                    }
                    //原有角色限定省为全部，则现限定省再为全部就没有意义了，但是原有角色限定省为全部，现限定省如为江苏省还是有意义的，因为有些业务认为大包小是取小（交集）而不是取大（并集），总之交并集不推荐在这里判断处理
                    //if((each.getProvince().equals("all"))&&(province.equals("all"))){
                    //只要在已有角色限定下，原限定或新限定有全部，则就认为有交集
                    if((each.getProvince().equals("all"))||(province.equals("all"))){
                    	return true;
                    }
                    //在省相同的情况下，原有角色限定市为全部，则现限定市再为全部就没有意义了，但是原有角色限定市为全部，现限定市如为苏州市还是有意义的，因为有些业务认为大包小是取小（交集）而不是取大（并集），总之交并集不推荐在这里判断处理
                    //if((each.getProvince().equals(province))&&((each.getCity().equals("all"))&&(city.equals("all")))){
                    //只要在已有角色限定下且省相同，原限定或新限定市有全部，则就认为有交集
                    if((each.getProvince().equals(province))&&((each.getCity().equals("all"))||(city.equals("all")))){
                    	return true;
                    }
                    //在省和市相同的情况下，原有角色限定区为全部，则现限定区再为全部就没有意义了，但是原有角色限定区为全部，现限定区如为姑苏区还是有意义的，因为有些业务认为大包小是取小（交集）而不是取大（并集），总之交并集不推荐在这里判断处理
                    //if((each.getProvince().equals(province)&&(each.getCity().equals(city))&&(((each.getDistrict().equals("all"))&&(district.equals("all")))))){
                    //只要在已有角色限定下且省市相同，原限定或新限定区有全部，则就认为有交集
                    if((each.getProvince().equals(province)&&(each.getCity().equals(city))&&(((each.getDistrict().equals("all"))||(district.equals("all")))))){
                    	return true;
                    }
                    //当有self时无法从角色定位到具体省、市、区，还有就是上面提到的，如角色限定省为全部，现限定省为self还是有意义的，总之如果有self，先不做校验
                }
            }
            return false;
		}
	}

	/**
	 * 角色限定新增业务层接口实现
	 * 
	 * @param roleRestrictAddRequest 角色限定新增请求参数
	 * @param user 用户信息
	 */
	@Transactional
	@Override
	public void roleRestrictAdd(RoleRestrictAddRequest roleRestrictAddRequest,User user) {
		RoleRestrict roleRestrict = new RoleRestrict();
		roleRestrict.setCreator(user.getUserId());
		roleRestrict.setCreateDate(new Date());
		roleRestrict.setUpdater(user.getUserId());
		roleRestrict.setUpdateDate(new Date());
		ObjectOperateUtil.copyProperties(roleRestrictAddRequest,roleRestrict);
		roleRestrictDao.insert(roleRestrict);
	}

	/**
	 * 角色限定详情业务层接口实现
	 * 
	 * @param roleRestrictId 角色限定ID
	 * @return RoleRestrict 角色限定信息
	 */
	@Override
	public RoleRestrict roleRestrictDetail(String roleRestrictId) {
		QueryWapper<RoleRestrict> queryWapper = new QueryWapper<>();
		queryWapper.eq(RoleRestrict::getId,roleRestrictId);
		queryWapper.eq(RoleRestrict::getDelFlag,0);
		return roleRestrictDao.selectOne(queryWapper);
	}

	/**
	 * 角色限定修改业务层接口实现
	 * 
	 * @param roleRestrictModifyRequest 角色限定修改请求参数
	 * @param user 用户信息
	 */
	@Transactional
	@Override
	public void roleRestrictModify(RoleRestrictModifyRequest roleRestrictModifyRequest,User user) {
		RoleRestrict roleRestrict = new RoleRestrict();
		roleRestrict.setUpdater(user.getUserId());
		roleRestrict.setUpdateDate(new Date());
		ObjectOperateUtil.copyProperties(roleRestrictModifyRequest,roleRestrict);
		roleRestrictDao.update(roleRestrict);
	}
	
	/**
	 * 角色限定删除业务层接口实现
	 * 
	 * @param roleRestrictId 角色限定ID
	 */
	@Transactional
	@Override
	public void roleRestrictDelete(String roleRestrictId) {
		String roleRestrictIds[] = roleRestrictId.split(CommonConstant.COMMA);
		for(String id:roleRestrictIds){
			roleRestrictDao.delete(id);
		}
	}

	/**
	 * 角色限定列表业务层接口实现
	 * 
	 * @param roleRestrictListRequest 角色限定列表请求参数
	 * @return Page 分页参数
	 */
	@Override
	public Page roleRestrictList(RoleRestrictListRequest roleRestrictListRequest) {
		List<RoleRestrictListResponse> list = roleRestrictDao.roleRestrictList(roleRestrictListRequest);
		Long count = roleRestrictDao.roleRestrictListCount(roleRestrictListRequest);
		Page page = new Page(roleRestrictListRequest,list,count);
		return page;
	}
	
	/**
	 * 合并省市区列表业务层接口实现
	 * <p>
	 * self不应该出现在list中，调用本合并省市区列表方法时，self应该已经被替换为具体的省市区了，
	 * 当然要在本方法中处理self也可以，那就需要多传一个User对象，把self替换为User对象中具体的省市区
	 * 
	 * @param list 省市区列表
	 * @return List<ProvinceCityDistrictAllResponse> 合并后的省市区列表
	 */
	@Override
	public List<ProvinceCityDistrictAllResponse> mergeSSQResponseList(List<ProvinceCityDistrictAllResponse> list){
		List<ProvinceCityDistrictAllResponse> outList = new ArrayList<>();
		Optional<ProvinceCityDistrictAllResponse> provinceOptional = list.stream().filter(e->"all".equals(e.getProvince())).findFirst();
		if(provinceOptional.isPresent()){
			outList.add(new ProvinceCityDistrictAllResponse("all","all","all"));
		}else{
			List<ProvinceCityDistrictAllResponse> newList = new ArrayList<>();
			Map<String,List<ProvinceCityDistrictAllResponse>> map = list.stream().collect(Collectors.groupingBy(ProvinceCityDistrictAllResponse::getProvince));
			Set<String> provinceSet = map.keySet();
			for(String each:provinceSet){
				List<ProvinceCityDistrictAllResponse> cityList = map.get(each);
				Optional<ProvinceCityDistrictAllResponse> cityOptional = cityList.stream().filter(e->"all".equals(e.getCity())).findFirst();
				if(cityOptional.isPresent()){
					newList.add(cityOptional.get());
				}else{
					newList.addAll(cityList);
				}
			}
			List<ProvinceCityDistrictAllResponse> newList2 = new ArrayList<>();
			Map<String,List<ProvinceCityDistrictAllResponse>> map2 = newList.stream().collect(Collectors.groupingBy(e->e.getCity()+e.getDistrict()));
			Set<String> citySet = map2.keySet();
			for(String each:citySet){
				List<ProvinceCityDistrictAllResponse> districtList = map2.get(each);
				Optional<ProvinceCityDistrictAllResponse> ksOptional = districtList.stream().filter(e->"all".equals(e.getDistrict())).findFirst();
				if(ksOptional.isPresent()){
					newList2.add(ksOptional.get());
				}else{
					newList2.addAll(districtList);
				}
			}
			outList = newList2;
		}
		return outList;
	}
	
	/**
	 * 获取权限代码业务层接口实现
	 * 
	 * @param bestMatchingPattern 最佳匹配路径
	 * @return String 权限代码
	 */
	@Override
	@Deprecated
	public String getPrivilegeCode(/*HttpServletRequest httpServletRequest,*/String bestMatchingPattern) {
		String privilegeCode = null;
		if(/*httpServletRequest*/bestMatchingPattern!=null){
			QueryWapper<Module> queryWapper = new QueryWapper<>();
			queryWapper.like(Module::getApiUrl,bestMatchingPattern);
			queryWapper.eq(Module::getDelFlag,0);
			List<Module> moduleList = moduleDao.selectList(queryWapper);
			if(!CollectionUtils.isEmpty(moduleList)){
				loop:for(int i=0;i<moduleList.size();i++){
					Module each = moduleList.get(i);
					String apiUrl = each.getApiUrl();
					if(!StringUtil.isEmpty(apiUrl)){
						String apiUrls[] = apiUrl.split(CommonConstant.COMMA);
						for(int j=0;j<apiUrls.length;j++){
							if(/*httpServletRequest.getServletPath()*/bestMatchingPattern.equals(apiUrls[j])){
								/**
								code    api_url
								a       /x
								b       /xa/y
								c       /xo/z,/x
								原则上code可以精确定位api_url，但是如果通过api_url去定位code，如上面的情况（一般下拉列表常见），那么只取首先匹配到的
								*/
								privilegeCode = each.getAlias();
								break loop;
							}
						}
					}
				}
			}
		}
		return privilegeCode;
	}
	
	/**
	 * 判断个人所在的省市区与组织是否重叠业务层接口实现
	 * 
	 * @param containsPersonal 是否包含个人限定
	 * @param user 用户信息
	 * @param list 省市区列表
	 * @return boolean true：个人和组织重叠；false：个人和组织无重叠
	 */
	@Override
	public boolean personalInOrganize(boolean containsPersonal,User user,List<ProvinceCityDistrictAllResponse> list) {
		boolean personalInOrganize = true;
		if(list.size()<1){
			personalInOrganize = false;
		}else{
			if(containsPersonal){//包含个人限定
				String province = user.getProvince();
				String city = user.getCity();
				String district = user.getDistrict();
				long count = list.stream().filter(
					e->("all".equals(e.getProvince()))
					   ||
					   ((e.getProvince().equals(province))&&("all".equals(e.getCity())))
					   ||
					   ((e.getProvince().equals(province)&&e.getCity().equals(city))&&("all".equals(e.getDistrict())))
					   ||
					   (String.join(CommonConstant.COMMA,e.getProvince(),e.getCity(),e.getDistrict())).equals(String.join(CommonConstant.COMMA,province,city,district))
				).count();
				if(count<1){//个人和组织无重叠
					personalInOrganize = false;
				}
			}
		}
		return personalInOrganize;
	}
	
	/**
	 * 个人和组织重叠时的省市区获取（不重叠时并集处理方式）业务层接口实现
	 * 
	 * @param user 用户信息
	 * @param personalInOrganize 个人所在的省市区与组织是否重叠（true：个人和组织重叠；false：个人和组织无重叠）
	 * @param list 省市区列表
	 * @return List<ProvinceCityDistrictAllResponse> 省市区列表
	 */
	@Override
	public List<ProvinceCityDistrictAllResponse> getOverlayList(User user,boolean personalInOrganize,List<ProvinceCityDistrictAllResponse> list) {
		if(CollectionUtils.isEmpty(list)){
			return Collections.emptyList();
		}
		if(personalInOrganize){
			return list;
		}else{
			list.add(new ProvinceCityDistrictAllResponse(user.getProvince(),user.getCity(),user.getDistrict()));
			return list;
		}
	}
	
	/**
	 * 角色限定信息获取业务层接口实现
	 * 
	 * @param user 用户信息
	 * @param bestMatchingPattern 最佳匹配路径
	 * @param clientType 客户端类型，参见OperationLog#clientType
	 * @return RoleRestrictResponse 角色限定信息
	 */
	@Override
	public RoleRestrictResponse roleRestrict(User user,/*HttpServletRequest httpServletRequest,*/String bestMatchingPattern,Integer clientType) {
		return this.roleRestrict(user,null,/*httpServletRequest*/bestMatchingPattern,clientType);
	}
	
	/**
	 * 角色限定信息获取业务层接口实现
	 * <p>
	 * 角色有类型限制，如全端通用或PC端，权限也有类型，如全端通用或移动端；
	 * 如果角色A全端通用，角色的权限为移动端，那么权限点在PC下应该是不受限制的，因为权限点仅限制在移动端
	 * 
	 * @param user 用户信息
	 * @param bestMatchingPattern 最佳匹配路径
	 * @param privilegeCode 权限代码（或叫alias别名）
	 * @param clientType 客户端类型，参见OperationLog#clientType
	 * @return RoleRestrictResponse 角色限定信息
	 */
	@Override
	public RoleRestrictResponse roleRestrict(User user,/*HttpServletRequest httpServletRequest,*/String bestMatchingPattern,String privilegeCode,Integer clientType) {
		if(StringUtil.isEmpty(privilegeCode)){
			privilegeCode = this.getPrivilegeCode(/*httpServletRequest*/bestMatchingPattern);//自动获取（不推荐）
		}
		boolean personalInOrganize = true;//个人是否包含在组织内
		boolean containsPersonal = false;//是否包含个人限定
		List<ProvinceCityDistrictAllResponse> list = new ArrayList<>();//省市区列表
		if(AdminConstant.ADMIN_USER_ID.equals(user.getUserId())){//管理员所有省市区
		    list.add(new ProvinceCityDistrictAllResponse("all","all","all"));
		}else{
		    List<UserRole> userRoleList = userRoleDao.selectList(new QueryWapper<UserRole>().eq(UserRole::getUserId,user.getUserId()));//获得该用户下的所有角色
		    if(CollectionUtils.isEmpty(userRoleList)) {//这里认为没有分配角色的用户无任何省市区权限
				list.clear();//空集合
		    }else{
				List<UserRole> newUserRole = new ArrayList<>();
				for(int i=0;i<userRoleList.size();i++){
					Role role = roleDao.selectOne(new QueryWapper<Role>().eq(Role::getRoleId,userRoleList.get(i).getRoleId()));
					if(role!=null&&(role.getType()==0||role.getType()!=clientType)){//根据登录的客户端类型动态处理
						continue;
					}
					List<RoleModule> roleModuleList = roleModuleDao.selectList(new QueryWapper<RoleModule>().eq(RoleModule::getRoleId,userRoleList.get(i).getRoleId()));
					if(!CollectionUtils.isEmpty(userRoleList)) {
						List<Module> modules = moduleDao.getModuleByModuleId(roleModuleList.stream().map(e->e.getModuleId()).toList());
						if(!CollectionUtils.isEmpty(modules)){
							String newPrivilegeCode = privilegeCode;
							if(newPrivilegeCode!=null){
								Optional<Module> optional = modules.stream().filter(e->(newPrivilegeCode.equals(e.getAlias()))&&(e.getType()==0||e.getType()==clientType)).findFirst();
								if(optional.isPresent()){
									newUserRole.add(userRoleList.get(i));
								}
							}
						}
					}
				}
				if(newUserRole.size()>0){
					userRoleList = newUserRole;//过滤掉非指定端的角色
				}
				if(CollectionUtils.isEmpty(userRoleList)){
					list.clear();//空集合
				}else{
				    for(int i=0;i<userRoleList.size();i++){
						List<RoleRestrict> roleRestrictList = roleRestrictDao.selectList(new QueryWapper<RoleRestrict>().eq(RoleRestrict::getRoleId,userRoleList.get(i).getRoleId()).eq(RoleRestrict::getDelFlag,0));
						if(CollectionUtils.isEmpty(roleRestrictList)){//没有角色限定就是不限定
							list.add(new ProvinceCityDistrictAllResponse("all","all","all"));
						}else{
						    for(RoleRestrict roleRestrict:roleRestrictList){
								if(roleRestrict.getRestrictWay().intValue()==1){//1：本人；2：非本人
								    containsPersonal = true;
								    break;
								}
						    }
						    for(RoleRestrict roleRestrict:roleRestrictList){
						    	list.add(new ProvinceCityDistrictAllResponse(roleRestrict.getProvince().replace("self",user.getProvince()),roleRestrict.getCity().replace("self",user.getCity()),roleRestrict.getDistrict().replace("self",user.getDistrict())));
						    }
						}
				    }
				    if(!CollectionUtils.isEmpty(list)){//多条数据合并处理（只有大于等于2才需要合并，1条合并没有意义）
				    	list = this.mergeSSQResponseList(list);
				    }
				}
		    }
		    personalInOrganize = personalInOrganize(containsPersonal,user,list);
		}
		//判断list是否为空，为空则限定个人（这个个人不是本省本市本区，而是个人的ID）；不为空则看个人是否包含在list内，如果个人包含在list内则取list；如果个人不包含在list内则看具体业务，一般认为取并集
		list = getOverlayList(user,personalInOrganize,list);
		//去重
		list = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()->new TreeSet<ProvinceCityDistrictAllResponse>(Comparator.comparing(e->String.join(CommonConstant.COMMA,e.getProvince(),e.getCity(),e.getDistrict())))),ArrayList::new));
		return new RoleRestrictResponse(containsPersonal,personalInOrganize,list);
	}

}
